package com.qcby.util;

import java.io.*;
import java.net.URL;
import java.util.Date;
import java.util.HashMap;
import java.util.Map;

import javax.annotation.PostConstruct;

import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;

import com.aliyun.oss.HttpMethod;
import com.aliyun.oss.OSSClient;
import com.aliyun.oss.model.*;

import lombok.Getter;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
@Getter
public class AliyunOssClientUtils {

    @Value("${zxk.oss.endpoint}")
    private String endpoint;

    @Value("${zxk.oss.bucketName}")
    private String bucketName;

    @Value("${zxk.oss.region}")
    private String region;

    @Value("${zxk.oss.accessKeyId}")
    private String accessKeyId;

    @Value("${zxk.oss.accessKeySecret}")
    private String accessKeySecret;

    @PostConstruct
    public void init() {

        END_POINT = this.endpoint;
        ACCESS_KEY_ID = this.accessKeyId;
        ACCESS_KEY_SECRET = this.accessKeySecret;
        BUCKET_NAME = this.bucketName;
        REGION = this.region;
        log.debug(String.format(">>> endpoint:%s,accessKeyId:%s,accessKeySecret:%s,bucketName:%s,region:%s", END_POINT, ACCESS_KEY_ID, ACCESS_KEY_SECRET, BUCKET_NAME, REGION));
    }

    public static String BUCKET_NAME = null;
    public static String ACCESS_KEY_ID = null;
    public static String ACCESS_KEY_SECRET = null;
    public static String END_POINT = null;
    public static String REGION = null;

    public static final Map<String, String> CONTENT_TYPE = new HashMap<>();

    /**
     * contentType集合.
     */
    static {
        CONTENT_TYPE.put(".bmp", "image/bmp");
        CONTENT_TYPE.put(".gif", "image/gif");
        CONTENT_TYPE.put(".jpeg", "image/jpeg");
        CONTENT_TYPE.put(".jpg", "image/jpeg");
        CONTENT_TYPE.put(".png", "image/png");
    }

    /**
     * 获取阿里云OSS客户端对象.
     *
     * @return ossClient
     */
    public static OSSClient getOssClient() {
        return new OSSClient(END_POINT, ACCESS_KEY_ID, ACCESS_KEY_SECRET);
    }

    /**
     * 创建文件夹.
     *
     * @param folder 文件夹名 如"test/"
     * @return 文件夹名
     */
    public static String createFolder(final String folder) {
        OSSClient ossClient = getOssClient();
        if (!ossClient.doesObjectExist(BUCKET_NAME, folder)) {
            ossClient.putObject(BUCKET_NAME, folder, new ByteArrayInputStream(new byte[0]));
            log.debug("创建目录成功：{}，{}", BUCKET_NAME, folder);
            OSSObject object = ossClient.getObject(BUCKET_NAME, folder);
            String fileDir = object.getKey();
            return fileDir;
        }
        log.debug("文件夹 {} 已存在，无需创建", folder);
        ossClient.shutdown();
        return folder;
    }


    /**
     * 根据key删除OSS服务器上的文件.
     *
     * @param path 带路径的文件名，路径使用/分隔
     */
    public static void deleteFile(final String path) {
        String bucketName = new String(BUCKET_NAME);
        OSSClient ossClient = getOssClient();
        if (ossClient.doesObjectExist(bucketName, path)) {
            ossClient.deleteObject(bucketName, path);
            log.debug("删除" + bucketName + "下的文件" + path + "成功");
        } else {
            log.debug(String.format("未找到路径%s 的文件，删除失败", path));
        }
        ossClient.shutdown();
    }

    /**
     * 上传文件至oss.
     *
     * @param file 上传文件
     * @param path 带路径的文件名，路径使用/分隔
     */
    public static void uploadObject(final File file, final String path) {
        log.debug(">>> 开始向oss上传文件！文件名为：" + file.getName() + "，路径为：" + path);
        String uploadName = null;
        String bucketName = new String(BUCKET_NAME);
        OSSClient ossClient = getOssClient();
        try {
            InputStream is = new FileInputStream(file);
            uploadName = file.getName();
            String fileExtension = uploadName.substring(uploadName.lastIndexOf("."));
            Long fileSize = file.length();
            ObjectMetadata metadata = new ObjectMetadata();
            metadata.setContentLength(is.available());
            metadata.setCacheControl("no-cache");
            metadata.setHeader("Pragma", "no-cache");
            metadata.setContentEncoding("utf-8");
            metadata.setContentType(CONTENT_TYPE.get(fileExtension.toLowerCase()));
            metadata.setContentDisposition("uploadName/filesize=" + uploadName + "/" + fileSize + "Byte.");
            PutObjectResult putResult = ossClient.putObject(bucketName, path, is, metadata);
            String eTag = putResult.getETag(); // 检查文件是否被篡改时，可以通过比较 ETag 值来判断文件内容是否发生变化
            log.debug(">>> 向oss上传文件结束！上传成功的文件eTag为：" + eTag);
        } catch (Exception e) {
            e.printStackTrace();
            log.error("上传阿里云OSS服务器异常." + e.getMessage(), e);
            throw new RuntimeException("上传文件云服务异常.");
        } finally {
            ossClient.shutdown();

        }
    }

    /**
     * 上传流文件至oss.
     *
     * @param content content
     *                this.bucketName
     * @param path    带路径的文件名，路径使用/分隔
     * @return String 文件夹名
     */
    public static String uploadObject(final byte[] content, final String path) {
        String url = null;
        String bucketName = new String(BUCKET_NAME);
        OSSClient ossClient = getOssClient();
        try (InputStream is = new ByteArrayInputStream(content);) {
            PutObjectResult putResult = ossClient.putObject(bucketName, path, is);
            url = END_POINT.replaceFirst("https://", "https://" + bucketName + ".") + "/" + path;
            putResult.getETag();
        } catch (Exception e) {
            e.printStackTrace();
            log.error("上传阿里云OSS服务器异常." + e.getMessage(), e);
            throw new RuntimeException("上传文件云服务异常.");
        } finally {
            ossClient.shutdown();
        }
        return url;
    }


    /*
     * TODO path是什么，数据库的pic就是什么
     *
     * TODO upload文件的时候，文件名path不能以'/'开头，所以数据库当中都是pic/fileName.jpg
     *
     */


    /**
     * 上传文件至oss,并设置为公共读.
     *
     * @param content content
     *                this.bucketName
     * @param path    带路径的文件名，路径使用/分隔
     */
    public static void uploadPublicObject(final byte[] content, final String path) {
        OSSClient ossClient = getOssClient();
        try {
            String bucketName = new String(BUCKET_NAME);
            InputStream is = new ByteArrayInputStream(content);
            ossClient.putObject(bucketName, path, is);
            ossClient.setObjectAcl(bucketName, path, CannedAccessControlList.PublicRead);
        } catch (Exception e) {
            log.error("上传阿里云OSS服务器异常." + e.getMessage(), e);
            throw new RuntimeException("上传文件云服务异常.");
        } finally {
            ossClient.shutdown();
        }
        ossClient.shutdown();
    }

    /**
     * 默认情况下，OSS Bucket中的文件是私有的，仅文件拥有者可访问。
     * 您可以使用OSS Java SDK生成带有过期时间的GET方法签名URL，以允许他人临时下载文件。在有效期内可多次访问，超期后需重新生成。
     *
     * @param path path
     * @return URL
     */
    public static URL uploadObjectUrl(final String path) {
        String bucketName = new String(BUCKET_NAME);
        GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, path, HttpMethod.PUT);
        // 10分钟
        Date expiration = new Date(new Date().getTime() + 600000);
        request.setExpiration(expiration);
        String fileExtension = path.substring(path.lastIndexOf("."));
        request.setContentType(CONTENT_TYPE.get(fileExtension.toLowerCase()));
        OSSClient ossClient = getOssClient();
        URL signedUrl = ossClient.generatePresignedUrl(request);
        log.debug("signed url for putObject: " + signedUrl);
        ossClient.shutdown();
        return signedUrl;
    }

    /**
     * 默认情况下，OSS Bucket中的文件是私有的，仅文件拥有者可访问。
     * 您可以使用OSS Java SDK生成带有过期时间的GET方法签名URL，以允许他人临时下载文件。在有效期内可多次访问，超期后需重新生成。
     * <p>
     * 自定义过期时间
     *
     * @param path        path
     * @param millisecond millisecond
     * @return URL
     */
    public static URL uploadObjectUrl(final String path, final Long millisecond) {
        String bucketName = new String(BUCKET_NAME);
        GeneratePresignedUrlRequest request = new GeneratePresignedUrlRequest(bucketName, path, HttpMethod.PUT);
        Date expiration = new Date(new Date().getTime() + millisecond);
        request.setExpiration(expiration);
        String fileExtension = path.substring(path.lastIndexOf("."));
        request.setContentType(CONTENT_TYPE.get(fileExtension.toLowerCase()));
        OSSClient ossClient = getOssClient();
        URL signedUrl = ossClient.generatePresignedUrl(request);
        log.debug("signed url for putObject: " + signedUrl);
        ossClient.shutdown();
        return signedUrl;
    }


    /**
     * 流文件下载.
     * <p>
     * this.bucketName
     *
     * @param path 带路径的文件名，路径使用/分隔
     * @return 二进制流
     */
    public static InputStream getObjectContent(final String path) {
        String bucketName = new String(BUCKET_NAME);
        OSSClient ossClient = getOssClient();
        if (!ossClient.doesObjectExist(bucketName, path)) {
            log.warn("找不到 " + path);
            throw new IllegalArgumentException(String.format("找不到 %s", path));
        }
        InputStream is = ossClient.getObject(bucketName, path).getObjectContent();
        ossClient.shutdown();
        return is;
    }


    /**
     * contentOf.
     *
     * @param ossObject ossObject
     * @return byte
     */
    private static byte[] contentOf(final OSSObject ossObject) {
        byte[] content = null;
        byte[] buff = new byte[100];
        int rc = 0;
        try (InputStream inputStream = ossObject.getObjectContent();
             ByteArrayOutputStream outStream = new ByteArrayOutputStream();) {
            while ((rc = inputStream.read(buff, 0, 100)) > 0) {
                outStream.write(buff, 0, rc);
            }
            content = outStream.toByteArray();
        } catch (IOException e) {
            e.printStackTrace();
            log.error("阿里云OSS服务器下载异常. " + e.getMessage(), e);
            throw new RuntimeException("文件云服务下载异常.");
        }
        return content;
    }


    /**
     * 经过图片处理后带签名认证的路径.
     *
     * @param style 图片样式,可为null
     * @param path  path
     * @return URL
     */
    private static URL generatePresignedUrlOf(final String style, final String path) {
        String bucketName = new String(BUCKET_NAME);
        Date expiration = new Date(new Date().getTime() + 600000);
        GeneratePresignedUrlRequest req = new GeneratePresignedUrlRequest(bucketName, path, HttpMethod.GET);
        req.setExpiration(expiration);
        if (style != null) {
            req.setProcess(style);
        }
        OSSClient ossClient = getOssClient();
        URL url = ossClient.generatePresignedUrl(req);
        ossClient.shutdown();
        return url;
    }

}
